﻿
#if !(UNITY_STANDALONE_WIN || UNITY_STANDALONE_LINUX || UNITY_STANDALONE_OSX || STEAMWORKS_WIN || STEAMWORKS_LIN_OSX)
#define DISABLESTEAMWORKS
#endif
#if !DISABLESTEAMWORKS
#endif
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public delegate void Callback();
public delegate void CallbackInt(int num);
public delegate void CallbackIntInt(int num1, int num2);
public delegate void CallbackFloat(float num);
public delegate void CallbackString(string value);
public delegate void CallbackVector3(Vector3 value);
public delegate void CallbackCollision2D(Collision2D value);
public delegate void CallbackCollider2D(Collider2D value);
public delegate void CallbackUri(Uri value);
class UtilsUnity
{
    public enum Align
    {
        ALIGN_LEFT_TOP,
        ALIGN_RIGHT_TOP,
        ALIGN_LEFT_BOTTOM,
        ALIGN_RIGHT_BOTTOM,
        ALIGN_CENTER,
    }
    public static bool IsMySelf(GameObject self, GameObject obj)
    {
        if (obj == null || self == null)
        {
            return false;
        }
        if (self == obj)
            return true;
        else if (obj.transform.parent)
        {
            return IsMySelf(self, obj.transform.parent.gameObject);
        }
        return false;
    }
    static public void Log(string log, bool isSystem)
    {
        if (!isSystem)
            Debug.Log(log);
    }

    static public Button GetButton(Transform transform, string path)
    {
        var ret = transform.Find(path);
        if (!ret)
            return null;
        return ret.GetComponent<Button>();
    }
    static public T GetChildComponent<T>(Transform transform, string path) where T: class
    {
        var ret = transform.Find(path);
        if (!ret)
            return null;
        return ret.GetComponent<T>();
    }
    /// <summary>
    /// 假定原点在右下角
    /// </summary>
    /// <param name="size"></param>
    /// <returns></returns>
    static public Vector3 GetPositionInScreen(Vector3 position, Vector2 size, Align align = Align.ALIGN_RIGHT_BOTTOM)
    {
        Vector3 mousePos = position + new Vector3(-10, -10);
        float scale = 1;
        var canvas = GetCanvas();
        if (null != canvas)
            scale = canvas.scaleFactor;
        size *= scale;

        switch (align)
        {
            case Align.ALIGN_LEFT_TOP:
                mousePos.x = Mathf.Clamp(mousePos.x, 0, Screen.width - size.x);
                mousePos.y = Mathf.Clamp(mousePos.y, size.y, Screen.height);
                break;
            case Align.ALIGN_RIGHT_TOP:
                mousePos.x = Mathf.Clamp(mousePos.x, size.x, Screen.width);
                mousePos.y = Mathf.Clamp(mousePos.y, size.y, Screen.height);
                break;
            case Align.ALIGN_LEFT_BOTTOM:
                mousePos.x = Mathf.Clamp(mousePos.x, 0, Screen.width - size.x);
                mousePos.y = Mathf.Clamp(mousePos.y, 0, Screen.height - size.y);
                break;
            case Align.ALIGN_RIGHT_BOTTOM:
                mousePos.x = Mathf.Clamp(mousePos.x, size.x, Screen.width);
                mousePos.y = Mathf.Clamp(mousePos.y, 0, Screen.height - size.y);
                break;
            case Align.ALIGN_CENTER:
                mousePos.x = Mathf.Clamp(mousePos.x, size.x / 2, Screen.width - size.x / 2);
                mousePos.y = Mathf.Clamp(mousePos.y, size.y / 2, Screen.height - size.y / 2);
                break;
        }
        return mousePos;
    }


    public static Canvas GetCanvas()
    {

        var foundCanvas = GameObject.FindGameObjectsWithTag("Canvas");
        foreach (var canvas in foundCanvas)
        {
            if (canvas.activeSelf && canvas.GetComponent<Canvas>())
                return canvas.GetComponent<Canvas>();
        }
        return null;
    }

    public static float GetCanvasScale()
    {
        float scale = 1;
        var canvas = GetCanvas();
        if (canvas)
            scale = canvas.scaleFactor;
        return scale;
    }


    public static void SetGUIActive(GameObject gameObject, bool isVisible)
    {
        // 是否可见
        float alpha = isVisible ? 1 : 0;
        // 是否可点击
        bool isClick = isVisible ? true : false;

        CanvasGroup canvasGroup = gameObject.GetComponent<CanvasGroup>();
        if (!canvasGroup)
        {
            canvasGroup = gameObject.AddComponent<CanvasGroup>();
        }

        canvasGroup.alpha = alpha;
        canvasGroup.blocksRaycasts = isClick;
    }
    public static bool IsGUIActive(GameObject gameObject)
    {
        CanvasGroup canvasGroup = gameObject.GetComponent<CanvasGroup>();
        if (!canvasGroup)
        {
            canvasGroup = gameObject.AddComponent<CanvasGroup>();
        }

        return canvasGroup.alpha > 0.01;
    }
    static public bool IsOnUI(Vector3 pos)
    {
        var canvas = GetCanvas();
        if (canvas == null)
            return false;
        GraphicRaycaster caster = canvas.GetComponent<GraphicRaycaster>();
        if (caster == null)
            return false;

        PointerEventData pData = new PointerEventData(EventSystem.current);

        pData.position = pos;
        pData.delta = Vector2.zero;
        pData.scrollDelta = Vector2.zero;

        List<UnityEngine.EventSystems.RaycastResult> canvasHits = new List<UnityEngine.EventSystems.RaycastResult>();
        caster.Raycast(pData, canvasHits);


        return canvasHits.Count > 0;
    }

    static public Sprite LoadSpriteByResouce(string url)
    {
        Sprite sprite = Resources.Load<Sprite>(url);
        if (sprite)
            return GameObject.Instantiate(sprite);
        return null;
    }
    /// <summary>
    /// 得到图片左下角的世界坐标
    /// </summary>
    /// <returns></returns>
    static public Vector3 GetLeftDownPoint(GameObject obj)
    {
        var shipSprite = obj.GetComponent<SpriteRenderer>().sprite;
        if (!shipSprite)
            return Vector3.zero;
        var scale = obj.transform.lossyScale;
        var halfSizeOfSprite = new Vector3(shipSprite.texture.width * scale.x / 2f, shipSprite.texture.height * scale.y / 2f);
        halfSizeOfSprite = halfSizeOfSprite / shipSprite.pixelsPerUnit;
        var leftDownPos = obj.transform.position - obj.transform.rotation * halfSizeOfSprite;
        return leftDownPos;
    }

    //   鼠标是否正在指住某个对象
    static public bool isOverUI(GameObject self)
    {
        /*
        var canvas = GetCanvas();
        if (canvas == null)
            return false;
        GraphicRaycaster caster = canvas.GetComponent<GraphicRaycaster>();
        if (caster == null)
            return false;

        PointerEventData pData = new PointerEventData(EventSystem.current);

        pData.position = Input.mousePosition;
        pData.delta = Vector2.zero;
        pData.scrollDelta = Vector2.zero;

        List<UnityEngine.EventSystems.RaycastResult> canvasHits = new List<UnityEngine.EventSystems.RaycastResult>();
        caster.Raycast(pData, canvasHits);

        if (canvasHits.Count < 0)
            return false;
        
        foreach (var i in canvasHits)
        {
            if(IsMySelf(self, i.gameObject))
            {
                return true;
            }
        }
        */
        var obj = CheckUI();
        if (!obj)
            return false;

        if (IsMySelf(self, obj))
        {
            return true;
        }
        return false;

        //GraphicRaycaster _raycaster = GameObject.FindObjectOfType<GraphicRaycaster>();
        //PointerEventData eventData = new PointerEventData(EventSystem.current);
        //eventData.pressPosition = Input.mousePosition;
        //eventData.position = Input.mousePosition;
        //List<RaycastResult> results = new List<RaycastResult>();
        //_raycaster.Raycast(eventData, results);
        //if (results.Count == 0)
        //    return null;
        //return results[0].gameObject;
    }

    static public bool hasComponent<T>(GameObject obj, bool checkToUp)
    {
        var com = obj.GetComponent<T>();
        if (com != null)
            return true;
        if (checkToUp)
        {
            if (obj.transform.parent)
                return hasComponent<T>(obj.transform.parent.gameObject, checkToUp);
        }
        else
        {
            for (var i = 0; i < obj.transform.childCount; ++i)
            {
                var child = obj.transform.GetChild(i);
                if (hasComponent<T>(child.gameObject, checkToUp))
                    return true;

            }
        }
        return false;
    }
    static public bool hasComponentByCheckParent<T>(GameObject obj)
    {
        var com = obj.GetComponent<T>();
        if (com != null)
            return true;
        if (obj.transform.parent)
        {
            hasComponentByCheckParent<T>(obj.transform.parent.gameObject);
        }
        return false;
    }
    //   鼠标是否正在指住某个对象
    static public bool isOverUI<T>(bool checkToUp = false)
    {
        var obj = CheckUI();
        if (!obj)
            return false;
        if (hasComponent<T>(obj, checkToUp))
        {
            return true;
        }
        return false;

    }
    /// <summary>
    /// 以IO方式进行加载
    /// </summary>
    static public Sprite LoadSpriteByIo(string url, Vector2 piovt, float pixelPerUnit = 100)
    {
        try
        {

            //创建文件读取流
            FileStream fileStream = new FileStream(url, FileMode.Open, FileAccess.Read);
            //创建文件长度缓冲区
            byte[] bytes = new byte[fileStream.Length];
            //读取文件
            fileStream.Read(bytes, 0, (int)fileStream.Length);

            //释放文件读取流
            fileStream.Close();
            //释放本机屏幕资源
            fileStream.Dispose();
            fileStream = null;

            //创建Texture
            int width = 256;
            int height = 256;
            Texture2D texture = new Texture2D(width, height);
            texture.LoadImage(bytes);
            //创建Sprite
            Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), piovt, pixelPerUnit);

            return sprite;
        }
        catch
        {
            Debug.LogError("LoadSpriteByIo exception in " + url);
            return null;
        }

    }

    //public static bool SaveJson(JsonData root, string path)
    //{
    //    string data = root.ToJson();
    //    using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write))
    //    {
    //        byte[] array = System.Text.Encoding.UTF8.GetBytes(data);
    //        fs.Write(array, 0, (int)array.Length);
    //        fs.Close();
    //    }
    //    return true;
    //}

    //public static JsonData LoadJson(string path)
    //{

    //    if (!File.Exists(path))
    //    {
    //        Debug.LogError("Utils.LoadJson() we can't found the file by path \"" + path + "\"");
    //        return null;
    //    }

    //    string data;
    //    using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
    //    {
    //        FileInfo info = new System.IO.FileInfo(path);
    //        byte[] array = new byte[info.Length];
    //        fs.Read(array, 0, (int)info.Length);
    //        fs.Close();
    //        data = System.Text.Encoding.Default.GetString(array);
    //    }
    //    if (data.Length == 0)
    //    {
    //        Debug.LogError("Utils.LoadJson() seems the file of path \"" + path + "\" is empty");
    //        return null;
    //    }
    //    JsonData jsonData = JsonMapper.ToObject(data);
    //    return jsonData;
    //}

    static public bool SaveTexture(string path, Texture2D texture)
    {
        byte[] data = null;
        try
        {
            data = texture.EncodeToPNG();
        }
        catch
        {
            Debug.Log("exception when texture encode to png at UtilsUnity::SaveTexture");
        }
        string url = path;
        try
        {
            FileStream fileStream = new FileStream(url, FileMode.OpenOrCreate, FileAccess.Write);
            fileStream.Write(data);
            //释放文件读取流
            fileStream.Close();
            //释放本机屏幕资源
            fileStream.Dispose();
            fileStream = null;
            return true;
        }
        catch
        {
            Debug.Log("File save fail on save Ship preview \"" + url + "\"");
            return false;
        }
    }
    public static bool IsPointerOverUIObject()

    {
#if UNITY_ANDROID

        PointerEventData eventData = new PointerEventData(EventSystem.current);

        eventData.position = Input.mousePosition;

        List<RaycastResult> results = new List<RaycastResult>();

        EventSystem.current.RaycastAll(eventData, results);

        return results.Count > 0;
#else
        return EventSystem.current.IsPointerOverGameObject();
#endif

    }
    public static bool IsPointerOverUIObject(Vector3 position)

    {
#if UNITY_ANDROID

        PointerEventData eventData = new PointerEventData(EventSystem.current);

        eventData.position = position;

        List<RaycastResult> results = new List<RaycastResult>();

        EventSystem.current.RaycastAll(eventData, results);

        return results.Count > 0;
#else
        return EventSystem.current.IsPointerOverGameObject();
#endif
    }

    static public GameObject CheckUI()
    {
        /// 鼠标左键没有点击，就不执行判断逻辑
        if (!IsPointerOverUIObject())
        {
            return null;
        }

        List<RaycastResult> list = new List<RaycastResult>();
        //场景中的EventSystem

        PointerEventData eventData = new PointerEventData(EventSystem.current);

        //鼠标位置
        eventData.position = Input.mousePosition;

        //调用所有GraphicsRacaster里面的Raycast，然后内部会进行排序，
        //直接拿出来，取第一个就可以用了
        EventSystem.current.RaycastAll(eventData, list);

        //这个函数抄的unity源码的，就是取第一个值
        var raycast = FindFirstRaycast(list);

        //获取父类中事件注册接口
        //如Button，Toggle之类的，毕竟我们想知道哪个Button被点击了，而不是哪张Image被点击了
        //当然可以细分为IPointerClickHandler, IBeginDragHandler之类细节一点的，各位可以自己取尝试
        var go = ExecuteEvents.GetEventHandler<IEventSystemHandler>(raycast.gameObject);

        //既然没拿到button之类的，说明只有Image挡住了，取点中结果即可
        if (go == null)
        {
            go = raycast.gameObject;
        }
        return go;
    }

    public static T DeepCopy<T>(T obj)
    {
        object retval;
        using (MemoryStream ms = new MemoryStream())
        {
            BinaryFormatter bf = new BinaryFormatter();
            //序列化成流
            bf.Serialize(ms, obj);
            ms.Seek(0, SeekOrigin.Begin);
            //反序列化成对象
            retval = bf.Deserialize(ms);
            ms.Close();
        }
        return (T)retval;
    }
    static private RaycastResult FindFirstRaycast(List<RaycastResult> candidates)
    {
        for (var i = 0; i < candidates.Count; ++i)
        {
            if (candidates[i].gameObject == null)
                continue;

            return candidates[i];
        }
        return new RaycastResult();
    }

    public static Texture2D GetCameraTexutre(Camera camera)
    {
        // The Render Texture in RenderTexture.active is the one
        // that will be read by ReadPixels.
        var currentRT = RenderTexture.active;
        RenderTexture.active = camera.targetTexture;

        // Render the camera's view.
        //camera.Render();
        camera.Render();
        // Make a new texture and read the active Render Texture into it.
        Texture2D image = new Texture2D(camera.targetTexture.width, camera.targetTexture.height);
        image.ReadPixels(new Rect(0, 0, camera.targetTexture.width, camera.targetTexture.height), 0, 0);
        image.Apply();

        // Replace the original active Render Texture.
        RenderTexture.active = currentRT;
        return image;
    }


    static public Vector3[] CalculateNewPos(Vector3 pos, Vector3 vel, Vector3 gra, float drag, int count)
    {
        //数据转化
        //物理刷新时间间隔
        var dt = Time.fixedDeltaTime;
        //所受重力转化为速度
        var graVel = gra * dt;
        //抵消掉阻力之后的受力系数（drag * dt：所受阻力）（不懂这个计算方式）
        var dragDt = 1 - drag * dt;
        //确保系数不为负
        dragDt = dragDt < 0 ? 0 : dragDt;
        //模拟速度位置
        for (int i = 0; i < count; i++)
        {
            //新速度 = (原速度 + 重力产生的速度) * 系数
            vel = (vel + graVel) * dragDt;
            //新位置 = 原位置 + 速度产生的位移 + 重力产生的位移（不懂0.5的作用）
            pos = pos + vel * dt + 0.5f * graVel * dt;
        }
        return new Vector3[] { pos, vel };
    }

}